#ifndef __I_LOCK_H__
#define __I_LOCK_H__

#include <zlib/lib/I_Ptr.h>
#include <zlib/lib/Ink_pthread.h>
#include <assert.h>

													/* Lock Macros */
/**
* 自动锁
* 上锁之后,离开作用域会自动解锁
*/
#define MUTEX_TRY_LOCK(_l, _m, _t) 			MutexTryLock _l(_m, _t)
#define MUTEX_LOCK(_l, _m, _t) 				MutexLock _l(_m, _t)

/**
* 显示锁
* 上锁之后需要显示解锁
*/
#define MUTEX_TAKE_TRY_LOCK(_m, _t)			Mutex_trylock(_m, _t)
#define MUTEX_TAKE_LOCK(_m, _t)				Mutex_lock(_m, _t)
#define MUTEX_TAKE_UNLOCK(_m, _t)			Mutex_unlock(_m, _t)

/*
	在多线程编程模型中,会出现多个线程对同一份资源的竞争,因此对互斥锁进行封装,以实现同步操作,保护数据.
	在异步编程模型中,有时也需要同步编程等方式,就有可能出现函数重入等情况,这时在当前线程中会出现一份资源会被当前锁多次.
	如下场景:
	比如,任务A持有资源锁A,然后创建了一个任务B,并将任务B投放到当前事件系统,任务B需要对资源A上锁.
		如果B任务被投放到当前线程的事件池中.(当前线程正在执行任务A,则必然不会产生锁竞争).等到A退出,锁释放, B持有锁进而执行.
		或者B任务在其他线程中执行,此时与A产生资源竞争(A任务应该在产生B并投放到异步事件系统之后,就立即退出),B持有锁进而执行.
			如果B持有锁失败,则重新调度.
	特殊场景:
	任务A持有资源所A,然后创建了一个资源锁B,如果B可以获取到自己所需要的资源,则同步调用,如果获取失败,则放入事件系统,等待异步执行.
		这样,不必所有的任务都通过异步回调的方式执行,以提高效率.(异步相比同步在于,避免对全局资源的竞争,但是如果没有锁竞争,
			异步回调需要额外执行许多操作,所以能同步调用,就不使用异步调用的方式)
		但是此时任务B需要资源A的锁,但是资源A被函数（任务A）所持有.这样就在当前线程中产生的锁竞争.
		funA{ Lock(A) -->call functionB }   functionB{ Lock(A) exec... }
		既然同属当前线程,那么A持有锁,此时调用B,B当然也可以去安全的访问资源A.
	ProxyMutex:同一线程内,对mutex多次上锁,只会执行一次lock,其余上锁操作都是对其锁的引用计数进行+1.其他线程上锁需要使用try-lock或者lock.

	另外,在异步编程模型中,都是基于异步回调来处理的.但是为了防止多个线程同时调用一个Continuation,所以需要多Continuation上锁.
	比如上锁TaskA创建了TaskB,TaskC.然后等待TaskB和TaskC执行完成后异步回调TaskA.为了实现线程安全操作,会将自己的锁分别传递给TaskB和TaskC.
	但是如果TaskA可能需要释放自己了,所以把锁释放了.在异步任务中，可能还存在TaskB和TaskC.那么此时上锁将出问题了.
	为了支持异步任务的取消操作,需要将ProxyMutex设置为Ptr类型.因此其需要继承RefCountObj,并且提供重写free方法.

	ProxyMutex不应该被直接调用,而是通过new_ProxyMutex来创建.
*/

//TODO:目前没有线程指针,暂时以void *来代替

class EThread;
typedef EThread *EThreadPtr;

class ProxyMutex : public RefCountObj
{
public:
	ink_mutex the_mutex;

	int nthread_holding;
	volatile EThreadPtr thread_holding;

	void free()
	{
		ink_mutex_destroy(&the_mutex);
		delete this;
	}

	ProxyMutex() : nthread_holding(0),thread_holding(0)
	{
	}

	void init()
	{
		ink_mutex_init(&the_mutex);
	}
};

inline ProxyMutex *
new_ProxyMutex()
{
	ProxyMutex *m = new ProxyMutex;
	m->init();
	return m;
}

inline bool
Mutex_lock(ProxyMutex *m, EThread *t)
{
	assert(t != 0);
	//TODO: 如果传递的t与当前所在线程不一致,abort.
	//assert(t == (EThread *)this_ethread());
	if(m->thread_holding != t) {
		ink_mutex_lock(&m->the_mutex);
		m->thread_holding = t;
	}

	m->nthread_holding++;
	return true;
}

inline bool
Mutex_trylock(ProxyMutex *m, EThread *t)
{
	assert(t != 0);
	//TODO:check call thread is current thread
	//assert(t == (EThread *)this_ethread())
	if(m->thread_holding != t) {
		if(!ink_mutex_try_lock(&m->the_mutex)) {
			return false;
		}

		m->thread_holding = t;
	}

	m->nthread_holding++;
	return true;			
}

inline void
Mutex_unlock(ProxyMutex *m, EThread *t)
{
	if(m->thread_holding) {
		assert(t == m->thread_holding);
		m->nthread_holding--;

		if(!m->nthread_holding) {
			assert(m->thread_holding);
			m->thread_holding = 0;
			ink_mutex_release(&m->the_mutex);
		}
	}
}


/* scoped lock class for ProxyMutex */
class MutexLock
{
private:
	Ptr<ProxyMutex> m;
public:
	MutexLock(ProxyMutex *am, EThread *t) : m(am)
	{
		Mutex_lock(m, t);
	}

	~MutexLock()
	{
		Mutex_unlock(m, m->thread_holding);
	}
};

class MutexTryLock
{
private:
	Ptr<ProxyMutex> m;
	bool locked;
public:
	MutexTryLock(ProxyMutex *am, EThread *t) : m(am),locked(false)
	{
		locked = Mutex_trylock(m , t);
	}

	~MutexTryLock()
	{
		if(locked)
			Mutex_unlock(m, m->thread_holding);
	}


	bool is_locked() const
	{
		return locked;
	}
};

#endif //__I_LOCK_H__
